package io.web.api.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.web.utils.Constant;
import io.web.utils.R;
import io.web.utils.SignUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * api调用签名判断
 */
public class AccessInterceptor implements HandlerInterceptor {

    /**
     * header签名属性名
     * accessKey
     * data: request data
     * timestamp
     * nonce
     * sign
     */
    private static final List<String> API_HEADER = Arrays.asList("accesskey", "data", "timestamp", "nonce", "sign");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!checkSign(request)) {
            errorHandler(response);
            return false;
        }
        return true;
    }

    private void errorHandler(HttpServletResponse response) throws IOException {
        R<?> r = new R<>(-1, null, "API接口认证失败");
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(r);
        response.getWriter().write(json);
    }

    private boolean checkSign(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String, Object> checkData = new HashMap<>();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            if (API_HEADER.contains(headerName)) {
                String header = request.getHeader(headerName);
                checkData.put(headerName, header);
            }
        }
        return checkSign(checkData);
    }

    /**
     * header签名属性名,请求头内容小写！
     * accessKey
     * data: request data
     * timestamp
     * nonce
     * sign
     */
    private boolean checkSign(Map<String, Object> checkData) {
        // get secretkey by accessKey from db, use spring cloud gateway instead
        Object accessKey = checkData.get("accesskey");
        if (accessKey == null || !Constant.accessKey.equals(accessKey.toString())) {
            return false;
        }

        Object content = checkData.get("data");
        Object sign = checkData.get("sign");
        if (content == null || sign == null
                || !SignUtils.verify(content.toString(), sign.toString(), Constant.secretKey)) {
            return false;
        }

        Object nonce = checkData.get("nonce");
        if (nonce == null) {
            return false;
        }

        Object timestamp = checkData.get("timestamp");
        // 5min 内有效
        if (timestamp == null || System.currentTimeMillis() - Long.parseLong(timestamp.toString()) > 5 * 60 * 1000) {
            return false;
        }
        return true;
    }
}
